package cn.yujiago.activiti.service.impl;

import cn.yujiago.activiti.constant.MessageConstant;
import cn.yujiago.activiti.dto.FileResult;
import cn.yujiago.activiti.dto.LayuiTableResult;
import cn.yujiago.activiti.dto.ResponseEntity;
import cn.yujiago.activiti.entity.User;
import cn.yujiago.activiti.service.WorkflowService;
import cn.yujiago.activiti.util.SessionUtils;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.identity.Group;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

@Service
public abstract class WorkflowServiceImpl<T> implements WorkflowService<T> {
	
	private static final Logger logger = LoggerFactory.getLogger(WorkflowServiceImpl.class);

	@Autowired
	private RuntimeService runtimeService;
	@Autowired
	private RepositoryService repositoryService;
	@Autowired
	private HistoryService historyService;
	@Autowired
	private TaskService taskService;
	@Autowired
	private FormService formService;
	@Autowired
	private IdentityService identityService;

	@Override
	public FileResult saveNewDeploy(FileInputStream fileInputStream, String filename) {
		try {
			ZipInputStream zipInputStream = new ZipInputStream(fileInputStream);
			Deployment deploy = repositoryService.createDeployment().name(filename).addZipInputStream(zipInputStream).deploy();
		} catch (Exception e) {
			logger.error("部署流程定义出错" , e);
			return new FileResult(500, MessageConstant.OPT_FAIL);
		}
		return new FileResult(200, MessageConstant.OPT_SUCCESS);
	}

	@Override
	public LayuiTableResult<Deployment> findDeploymentList(int page, int limit) {
		List<Deployment> list = repositoryService
				.createDeploymentQuery()
				.orderByDeploymenTime()
				.asc()
				.listPage(page-1, limit);

		long count = repositoryService
				.createDeploymentQuery().count();

		LayuiTableResult<Deployment> result = new LayuiTableResult<>();
		result.setCode(0);
		result.setMsg(MessageConstant.QUERY_SUCCESS);
		result.setData(list);
		result.setCount(count);

		return result;
	}

	@Override
	public LayuiTableResult<ProcessDefinition> findProcessDefinitionList(int page, int limit) {
		// 根据版本号升序排列
		List<ProcessDefinition> list = repositoryService
				.createProcessDefinitionQuery()
				.orderByProcessDefinitionVersion()
				.asc()
				.listPage(page-1, limit);

		long count = repositoryService
				.createProcessDefinitionQuery().count();

		LayuiTableResult<ProcessDefinition> result = new LayuiTableResult<>();
		result.setCode(0);
		result.setMsg(MessageConstant.QUERY_SUCCESS);
		result.setData(list);
		result.setCount(count);

		return result;
	}

	@Override
	public LayuiTableResult<HistoricProcessInstance> findProcessInstanceList(int page, int limit) {

		List<HistoricProcessInstance> list = historyService
				.createHistoricProcessInstanceQuery()
				.orderByProcessInstanceEndTime()
				.desc()
				.listPage(page - 1, limit);

		long count = historyService
				.createHistoricProcessInstanceQuery()
				.count();

		LayuiTableResult<HistoricProcessInstance> result = new LayuiTableResult<>();
		result.setCode(0);
		result.setMsg(MessageConstant.QUERY_SUCCESS);
		result.setData(list);
		result.setCount(count);

		return result;
	}

	@Override
	public LayuiTableResult<Task> findTaskListByName(int page, int limit, String username) {

		List<Task> list = taskService
				.createTaskQuery()
				.taskAssignee(username)
				.orderByTaskCreateTime()
				.asc()
				.listPage(page-1, limit);

		long count = taskService
				.createTaskQuery().taskAssignee(username).count();

		LayuiTableResult<Task> result = new LayuiTableResult<>();
		result.setCode(0);
		result.setMsg(MessageConstant.QUERY_SUCCESS);
		result.setData(list);
		result.setCount(count);

		return result;
	}

	@Override
	public List<Group> findGroupList() {
		return identityService.createGroupQuery().orderByGroupId().asc().list();
	}

	@Override
	public LayuiTableResult<Task> findTaskListByGroup(int page, int limit, String groupname) {

		List<Task> list = taskService
				.createTaskQuery()
				.taskCandidateGroup(groupname)
				.orderByTaskCreateTime()
				.asc()
				.listPage(page-1, limit);

		long count = taskService
				.createTaskQuery().taskCandidateGroup(groupname).count();

		LayuiTableResult<Task> result = new LayuiTableResult<>();
		result.setCode(0);
		result.setMsg(MessageConstant.QUERY_SUCCESS);
		result.setData(list);
		result.setCount(count);

		return result;
	}

	@Override
	public ResponseEntity claimTaskByTaskId(String taskId) {

		User user = SessionUtils.get();

		taskService.claim(taskId, user.getUsername());
		return ResponseEntity.build(200, MessageConstant.OPT_SUCCESS);
	}

	@Override
	public String findBusinessKeyByTaskId(String taskId) {

		// 通过任务id获取任务对象
		Task task = this.getTaskService().createTaskQuery().taskId(taskId).singleResult();
		// 通过任务对象获取当前流程实例id
		String processInstanceId = task.getProcessInstanceId();
		// 通过流程实例id获取流程实例对象，从而获取businessKey
		String businessKey = this.getRuntimeService().createProcessInstanceQuery().processInstanceId(processInstanceId)
				.singleResult().getBusinessKey();

		return businessKey;
	}

	@Override
	public List<String> findOutComeListByTaskId(String taskId) {
		ArrayList<String> outComeList = new ArrayList<>();
		// 通过任务id获取任务对象
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		// 通过任务对象获取流程定义id
		String processDefinitionId = task.getProcessDefinitionId();
		// 通过任务对象获取流程实例id
		String processInstanceId = task.getProcessInstanceId();
		// 通过流程实例id获取流程实例对象
		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
				.processInstanceId(processInstanceId).singleResult();
		// 通过流程定义id获取流程定义实体（对应.bpmn文件）
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService
				.getProcessDefinition(processDefinitionId);
		// 通过流程实例获取当前活动id
		String activityId = processInstance.getActivityId();
		// 通过当前活动id获取当前活动对象
		ActivityImpl activity = processDefinitionEntity.findActivity(activityId);
		// 获取当前活动结束后的连线名称
		List<PvmTransition> list = activity.getOutgoingTransitions();
		for (PvmTransition pvmTransition : list) {
			// 获取连线名称
			String name = (String) pvmTransition.getProperty("name");
			if (StringUtils.isNotEmpty(name)) {
				outComeList.add(name);
			} else {
				outComeList.add("默认提交");
			}
		}
		return outComeList;
	}

	@Override
	public List<Comment> findCommentByTaskId(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		String processInstanceId = task.getProcessInstanceId();
		return taskService.getProcessInstanceComments(processInstanceId);
	}

	@Override
	public InputStream findImageInputStream(String processDefinitionId) {
		//根据流程定义id查询流程定义对象
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(processDefinitionId).singleResult();
		// 取得图片输入流
		return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
				processDefinition.getDiagramResourceName());
	}

	@Override
	public Map<String, Object> getCurrentImageInfoByTaskId(String taskId) {
		Map<String, Object> map = new HashMap<>();

		// 获取流程定义
		ProcessDefinition processDefinition = this.findProcessDefinitionByTaskId(taskId);
		// 获取当前活动节点的坐标
		Map<String, Integer> coordinateMap = this.findCoordinateByTaskId(taskId);

		map.put("processDefinitionId", processDefinition.getId());
		map.put("deploymentId", processDefinition.getDeploymentId());
		map.put("imageName", processDefinition.getDiagramResourceName());
		map.put("coordinate", coordinateMap);

		return map;
	}

	@Override
	public ProcessDefinition findProcessDefinitionByTaskId(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		String processDefinitionId = task.getProcessDefinitionId();
		return repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId)
				.singleResult();
	}

	@Override
	public Map<String, Integer> findCoordinateByTaskId(String taskId) {
		HashMap<String, Integer> map = new HashMap<>();
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		String processDefinitionId = task.getProcessDefinitionId();
		String processInstanceId = task.getProcessInstanceId();
		// 获取流程定义的实体对象（对应.bpmn文件）
		ProcessDefinitionEntity entity = (ProcessDefinitionEntity) repositoryService
				.getProcessDefinition(processDefinitionId);
		// 获取流程实例
		ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
				.singleResult();
		String activityId = pi.getActivityId();
		// 获取当前活动对象
		ActivityImpl activity = entity.findActivity(activityId);
		// 获取坐标
		map.put("x", activity.getX());
		map.put("y", activity.getY());
		map.put("width", activity.getWidth());
		map.put("height", activity.getHeight());
		return map;
	}

	@Override
	public Map<String, Object> getCurrentImageInfoByBusinessKey(String businessKey) {
		Map<String, Object> map = new HashMap<>();

		// 获取流程定义
		ProcessDefinition processDefinition = this.findProcessDefinitionByBusinessKey(businessKey);
		// 获取当前活动节点的坐标
		Map<String, Integer> coordinateMap = this.findCoordinateByBusinessKey(businessKey);

		map.put("processDefinitionId", processDefinition.getId());
		map.put("deploymentId", processDefinition.getDeploymentId());
		map.put("imageName", processDefinition.getDiagramResourceName());
		map.put("coordinate", coordinateMap);

		return map;
	}

	@Override
	public ProcessDefinition findProcessDefinitionByBusinessKey(String businessKey) {
		ProcessInstance processInstance = this.getRuntimeService().createProcessInstanceQuery()
				.processInstanceBusinessKey(businessKey).singleResult();
		String processDefinitionId = processInstance.getProcessDefinitionId();
		return this.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
	}

	@Override
	public Map<String, Integer> findCoordinateByBusinessKey(String businessKey) {

		ActivityImpl act = null;
		ProcessInstance instance = this.getRuntimeService().createProcessInstanceQuery().processInstanceBusinessKey(businessKey)
				.singleResult();
		String processDefinitionId = instance.getProcessDefinitionId();
		// 获取流程定义实体
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) this.getRepositoryService()
				.getProcessDefinition(processDefinitionId);
		List<ActivityImpl> activities = processDefinitionEntity.getActivities();

		ExecutionEntity executionEntity = (ExecutionEntity) this.getRuntimeService().createExecutionQuery()
				.processInstanceBusinessKey(businessKey).singleResult();
		// 获取当前任务执行到哪个活动节点
		String activityId = executionEntity.getActivityId();
		for (ActivityImpl actity : activities) {
			if (activityId.equals(actity.getId())) {
				act = actity;
				break;
			}
		}

		// 获取当前活动节点的坐标
		HashMap<String, Integer> map = new HashMap<>();
		map.put("x", act.getX());
		map.put("y", act.getY());
		map.put("width", act.getWidth());
		map.put("height", act.getHeight());

		return map;
	}

	/**
	 * getter、setter
	 */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	public RuntimeService getRuntimeService() {
		return runtimeService;
	}

	public void setRuntimeService(RuntimeService runtimeService) {
		this.runtimeService = runtimeService;
	}

	public RepositoryService getRepositoryService() {
		return repositoryService;
	}

	public void setRepositoryService(RepositoryService repositoryService) {
		this.repositoryService = repositoryService;
	}

	public HistoryService getHistoryService() {
		return historyService;
	}

	public void setHistoryService(HistoryService historyService) {
		this.historyService = historyService;
	}

	public TaskService getTaskService() {
		return taskService;
	}

	public void setTaskService(TaskService taskService) {
		this.taskService = taskService;
	}

	public FormService getFormService() {
		return formService;
	}

	public void setFormService(FormService formService) {
		this.formService = formService;
	}

	public IdentityService getIdentityService() {
		return identityService;
	}

	public void setIdentityService(IdentityService identityService) {
		this.identityService = identityService;
	}
}
